package com.example.backblog.controller.front.user;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.ShearCaptcha;
import cn.hutool.core.date.DateUtil;
import com.example.backblog.dto.user.LoginForm;
import com.example.backblog.dto.user.RegisterForm;
import com.example.backblog.entity.user.User;
import com.example.backblog.exception.user.VerifyException;
import com.example.backblog.repository.user.UserRepository;
import com.example.backblog.service.fileupload.FileService;
import com.example.backblog.vo.ApiResult;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Date;

import static com.example.backblog.util.ConstantUtil.USER_SESSION;
import static com.example.backblog.util.ConstantUtil.VERIFY_CODE_NAME;

@RestController
@RequestMapping("/user/login")
public class LoginController {
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private FileService fileService;


    // 验证码生成时间键名
    private final static String VERIFY_CODE_GENERATE_TIME = "verify_code_time";
    @PostMapping
    public ApiResult loginShiro(HttpSession session,@RequestBody LoginForm loginForm) throws Exception {
        // 验证验证码
        verify(session,loginForm.getVerifyCode());
        String username = loginForm.getUserName();
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, loginForm.getPassword());
        try {
            // 设置session
            subject.login(usernamePasswordToken);
            // 更新登录时间
            User user = userRepository.findFirstByNameEquals(username);
            user.setLastLogin(new Date());
            userRepository.save(user);
            return ApiResult.success();
        } catch (AuthenticationException e) {
            throw new Exception("登录失败");
        }
    }
    @PostMapping("/loginWithoutVerify")
    public ApiResult login(HttpSession session,@RequestBody LoginForm loginForm) throws Exception {
        String username = loginForm.getUserName();
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, loginForm.getPassword());
        try {
            // 设置session
            subject.login(usernamePasswordToken);
            // 更新登录时间
            User user = userRepository.findFirstByNameEquals(username);
            user.setLastLogin(new Date());
            userRepository.save(user);
            return ApiResult.success();
        } catch (AuthenticationException e) {
            throw new Exception("登录失败");
        }
    }
    @GetMapping("/test")
    @RequiresRoles("vip")
    public ApiResult test(Long parentId){
        return null;
    }
    @GetMapping("/test2")
    @RequiresPermissions("blog:get")
    public ApiResult test2(){
        return ApiResult.success();
    }
    /**
     * 验证验证码
     * @param session
     * @param verifyCode
     */
    private void verify(HttpSession session, String verifyCode) throws VerifyException {
        String shearCaptcha = (String) session.getAttribute(VERIFY_CODE_NAME);
        Date lastTime = (Date) session.getAttribute(VERIFY_CODE_GENERATE_TIME);
        // 不能超过一分钟
        if (DateUtil.compare(new Date(),lastTime)<0) {
            throw new VerifyException("验证码过期");
        }
        if (!shearCaptcha.equals(verifyCode)) {
            throw new VerifyException("验证码错误");
        }
    }

    @GetMapping("/verifyCode")
    public void verifyCode(HttpSession session, HttpServletResponse response) throws IOException {
        // 设置验证码
        ShearCaptcha shearCaptcha = CaptchaUtil.createShearCaptcha(400, 100);
        session.setAttribute(VERIFY_CODE_NAME,shearCaptcha.getCode());
        session.setAttribute(VERIFY_CODE_GENERATE_TIME,new Date());
        // 输出流
        shearCaptcha.write(response.getOutputStream());
        return;
    }

    @PostMapping("/register")
    public ApiResult register(HttpSession session,@RequestBody RegisterForm registerForm) throws VerifyException {
        // 注册用户
        // 生成盐,默认长度 16 位
        String salt = new SecureRandomNumberGenerator().nextBytes().toString();
        // 设置 hash 算法迭代次数
        int times = 2;
        // 得到 hash 后的密码
        String encodedPassword = new SimpleHash("md5", registerForm.getPassword(), salt, times).toString();
        User user = User.builder().name(registerForm.getUserName()).password(encodedPassword)
                .salt(salt).build();
        verify(session,registerForm.getVerifyCode());
        userRepository.save(user);
        session.setAttribute(USER_SESSION,user.getId());
        return ApiResult.success();
    }
    @PostMapping("/register/withoutVerify")
    public ApiResult registerWithoutVerifyCode(HttpSession session,@RequestBody RegisterForm registerForm) throws VerifyException {
        // 注册用户
        // 生成盐,默认长度 16 位
        String salt = new SecureRandomNumberGenerator().nextBytes().toString();
        // 设置 hash 算法迭代次数
        int times = 2;
        // 得到 hash 后的密码
        String encodedPassword = new SimpleHash("md5", registerForm.getPassword(), salt, times).toString();
        User user = User.builder().name(registerForm.getUserName()).password(encodedPassword)
                .salt(salt).build();
        userRepository.save(user);
        session.setAttribute(USER_SESSION,user.getId());
        return ApiResult.success();
    }
    @ResponseBody
    @RequiresAuthentication
    @GetMapping("logout")
    public ApiResult logout() {
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        String message = "成功登出";
        return ApiResult.success(message);
    }
}
